#ifndef IO_HPP
#define IO_HPP

#include "typedef.hpp"
#include <iostream>
#include <fstream>

enum IOTYPE : uint8_t
{
	IN,	  // 串行输入
	OUT,  // 串行输出
	FIN,  // 文件输入
	FOUT, // 文件输出
	FIO	  // 文件输入输出
};

class IODevice
{
public:
	IOSTATUS status;
	IOTYPE type;
	IODevice() : status(DONE), type(FIO) {}
	virtual void open(std::string, IOTYPE) = 0;
	virtual void close() = 0;
	virtual void read() = 0;
	virtual void write() = 0;
	virtual void set(uint64_t) = 0;
	void setStatus(IOSTATUS);
	virtual uint64_t get() const = 0;
	IOSTATUS getStatus() { return status; }
	void start(uint64_t &);
};
class SerialIn : public IODevice
{
private:
	char value;

public:
	SerialIn() { type = IN; }
	void open(std::string = "", IOTYPE = IN);
	void close() { std::cin.clear(); }
	void read();
	void write();
	void set(uint64_t val = 0) { status = FAIL; }
	uint64_t get() const { return value; }
};
class SerialOut : public IODevice
{
private:
	char value;

public:
	SerialOut() { type = OUT; }
	void open(std::string = "", IOTYPE = OUT);
	void close() { std::cout.clear(); }
	void read();
	void write();
	void set(uint64_t val) { value = val & 0xff; }
	uint64_t get() const { return value; }
};
class FileIn : public IODevice
{
private:
	FILE *fin;
	uint64_t faddr, maddr;

public:
	FileIn() = default;
	void open(std::string, IOTYPE = FIN);
	void close() { fclose(fin); }
	void read();
	void write();
	void set(uint64_t val)
	{
		if (status == SET0)
			faddr = val;
		else if (status == SET1)
			maddr = val;
	}
	uint64_t get() const
	{
		if (status == GET0)
			return maddr;
		else if (status == GET1)
			return faddr;
		else
			return 0;
	}
};
class FileOut : public IODevice
{
private:
	FILE *fout;
	uint64_t faddr, maddr;

public:
	FileOut() = default;
	void open(std::string, IOTYPE = FOUT);
	void close() { fclose(fout); }
	void read();
	void write();
	void set(uint64_t val)
	{
		if (status == SET0)
			faddr = val;
		else if (status == SET1)
			maddr = val;
	}
	uint64_t get() const
	{
		if (status == GET0)
			return maddr;
		else if (status == GET1)
			return faddr;
		else
			return 0;
	}
};
class FileIO : public IODevice
{
private:
	FILE *file;
	uint64_t faddr, maddr;

public:
	FileIO() = default;
	void open(std::string, IOTYPE = FIO);
	void close() { fclose(file); }
	void read();
	void write();
	void set(uint64_t val)
	{
		if (status == SET0)
			faddr = val;
		else if (status == SET1)
			maddr = val;
	}
	uint64_t get() const
	{
		if (status == GET0)
			return maddr;
		else if (status == GET1)
			return faddr;
		else
			return 0;
	}
};

class IOChannel
{
private:
	std::map<uint16_t, IODevice *> mIO;
	IOMessage *msg;

public:
	IOChannel() = default;
	IOChannel(IOMessage *msg) : msg(msg) {}
	~IOChannel() = default;
	void init();
	void mount(std::string fname, IOTYPE);
	void umount(uint16_t);
	void run();
};

#endif